﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.IO.IsolatedStorage;
using System.Linq;
using System.Net;
using System.Reflection;
using System.ServiceModel.Syndication;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using System.Xml;
using System.Xml.Linq;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Tasks;
using Microsoft.Phone.Shell;
using Microsoft.Phone.Info;
using TagCloudNewsLibrary.Model;
using Windows.Graphics;

namespace TagCloudNewsLibrary
{
    public delegate void LoadCompletedEventHandler();
    public delegate void LoadFailedEventHandler();

    public class TagDataService
    {
        //for accessing this service
        private static TagDataService _current;
        private bool _loaded = false;
        private bool _headless=false;
        private int _scrollPosition;

        private static Brush unlikedTagColour;
        private static Brush likedTagColour;
        private static Brush readStoryColour;

        //data
        private Dictionary<string, Story> _stories = new Dictionary<string, Story>();
        private Dictionary<string, Tag> _tags = new Dictionary<string, Tag>();
        private List<string> _likedTags = new List<string>();

        //ranges
        private int _upper;
        private int _lower;

        //properties
        public LoadCompletedEventHandler LoadCompletedEvent { get; set; }
        public LoadFailedEventHandler LoadFailedEvent { get; set; }
        public string XmlUrl { get; set; }

        void SetColours()
        {
            unlikedTagColour = new SolidColorBrush(Colors.Gray);

             likedTagColour = new SolidColorBrush(Colors.Red);
             readStoryColour = new SolidColorBrush(Colors.Gray);
        }

        public TagDataService()
        {
            LoadLikedTags();
            LoadScrollPosition();

            Action a = new Action(this.SetColours);
            Deployment.Current.Dispatcher.BeginInvoke(a);
        }

        public static TagDataService Current
        {
            get
            {
                if (_current == null)
                    _current = new TagDataService();

                return _current;
            }
        }


        public void SetScrollPos(int pos)
        {
            _scrollPosition = pos;
        }

        public int GetScrollPos()
        {
            return _scrollPosition;
        }

        public string EscapeTag(string tag)
        {
            return Uri.EscapeUriString(tag.Replace("&", "^^"));
        }

        public string UnescapeTag(string tag)
        {
            return Uri.UnescapeDataString(tag.Replace("^^","&" ));
        }

        private string GetAppVersionNo()
        {
            var nameHelper = new AssemblyName(Assembly.GetExecutingAssembly().FullName);

            return nameHelper.Version.ToString();
        }

        public AboutModel About()
        {
            AboutModel a = new AboutModel();

            a.Version = "Version : " + GetAppVersionNo();
            return a;
        }

        public void UpdateTiles()
        {
            try
            {
                //examples
                //t.NavigationUri=/
                //t.NavigationUri=/StoriesPage.xaml?tag=Festival

                Debug.WriteLine("ActiveTiles.Count={0}", ShellTile.ActiveTiles.Count());

                string wideOne;
                string wideTwo;
                string wideThree;

                foreach (var tile in ShellTile.ActiveTiles)
                {
                    int count = 0;
                    wideOne = "";
                    wideTwo = "";
                    wideThree = "";

                    Debug.WriteLine("t.NavigationUri={0}", tile.NavigationUri);

                    if (tile.NavigationUri.ToString().EndsWith("/"))
                    {
                        //main tile
                        List<string> tags = new List<string>();

                        var stories = GetUnreadStoriesForLikedTags();
                        count = stories.Count();

                        if (count > 0)
                        {
                            Random rnd = new Random();
                            int r = rnd.Next(1, stories.Count());

                            Story selectedStory = stories.ToList()[r];
                            wideOne = selectedStory.Title;
                            wideTwo = selectedStory.PublishDate.ToString("g");
                            wideThree =String.Join(",", selectedStory.Tags);
                        }
                        else
                        {
                            wideOne = "";
                            wideTwo = "";
                            wideThree = "";
                        }
                    }
                    else
                    {
                        //tag tile
                        string u = tile.NavigationUri.ToString();
                        string tag =TagDataService.Current.UnescapeTag( u.Substring(u.LastIndexOf("=") + 1));

                        var s= GetUnreadStoriesForTag(tag);
                        count = s.Count();
                        if (count > 0)
                        {
                            wideOne = s.Last().Title;
                            wideTwo = s.Last().PublishDate.ToString("g");
                        }
                        else
                        {
                            wideOne = "";
                            wideTwo = "";
                        }
                    }

                    IconicTileData newTile = new IconicTileData();

                    newTile.Count = count;
                    newTile.WideContent1 = wideOne;
                    newTile.WideContent2 = wideTwo;
                    newTile.WideContent3 = wideThree;

                    newTile.SmallIconImage = new Uri(@"110x110.png", UriKind.Relative);
                    newTile.IconImage = new Uri(@"202x202.png", UriKind.Relative);

                    tile.Update(newTile);
                }
            }
            catch (Exception)
            {
            }
        }

        private BitmapImage GetImageFromIcon(string iconUrl)
        {
            // http://www.google.com/s2/favicons?   
            // www.images.xlr8r.com
            BitmapImage result = new BitmapImage();

            if (iconUrl == "NONE")
            {
                WriteableBitmap wb = new WriteableBitmap(16, 16);
                Canvas can = new Canvas();
                can.Background = new SolidColorBrush(Colors.White); ;
                can.Width = 16;
                can.Height = 16;

                wb.Render(can, null);

                using (MemoryStream ms = new MemoryStream())
                {
                    wb.SaveJpeg(ms, (int)16, (int)16, 0, 100);

                    result.SetSource(ms);
                }
            }
            else
            {
                string temp = iconUrl;
                temp = temp.Replace(@"http://", "");
                temp = temp.Substring(0, temp.IndexOf(@"/"));

                if (!temp.StartsWith("www"))
                {
                    temp = "www." + temp;
                }

                if (temp.Count(f => f == '.') > 2)
                {
                    int start = temp.IndexOf(".");
                    int end = temp.IndexOf(".", start + 1);
                    temp = temp.Remove(start, end - start);
                }
                Debug.WriteLine(temp);
                result = new System.Windows.Media.Imaging.BitmapImage(new Uri(@"http://www.google.com/s2/favicons?domain=" + temp));
            }
            return result;
        }

        public void ForceReload()
        {
            _loaded = false;
            RefreshNewsFeed(false);
        }

        public void RefreshNewsFeed(bool headless)
        {
            _headless=headless;

            if (!_loaded)
            {
                // WebClient is used instead of HttpWebRequest in this code sample because 
                // the implementation is simpler and easier to use, and we do not need to use 
                // advanced functionality that HttpWebRequest provides, such as the ability to send headers.
                WebClient xmlWebClient = new WebClient();
                WebClient visitWebClient = new WebClient();

                // Subscribe to the DownloadStringCompleted event prior to downloading the RSS feed.
                xmlWebClient.DownloadStringCompleted += new DownloadStringCompletedEventHandler(webClient_XmlDownloadStringCompleted);


                string xmlUrl = GetConfigVAlue("XmlUrl");
                string appName = GetConfigVAlue("VisitKey");

                // Download the RSS feed. DownloadStringAsync was used instead of OpenStreamAsync because we do not need 
                // to leave a stream open, and we will not need to worry about closing the channel. 
                xmlWebClient.DownloadStringAsync(new System.Uri(xmlUrl));

#if DEBUG
                string devId = "DEBUG";
#else
                byte[] myDevId = (byte[])DeviceExtendedProperties.GetValue("DeviceUniqueId");
                string devId = Convert.ToBase64String(myDevId);
#endif
                string visitUrl = String.Format("http://www.thirdprize.org/code/RegisterVisitV1?user={0}&appName={1}&appVersion={2}&platform=wp", devId, appName, GetAppVersionNo());
                visitWebClient.DownloadStringAsync(new System.Uri(visitUrl));
            }
            else
            {
                LoadCompletedEvent();
            }
        }

        /// <summary>
        /// When called from the app it gets its values fom the resource file.  when called from the background task it gets it from the settings
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        private static string GetConfigVAlue(string key)
        {
            //get the url
            IsolatedStorageSettings settings = IsolatedStorageSettings.ApplicationSettings;
            string result;

            if (settings.Contains(key))
            {
                result = settings[key] as string;
            }
            else
            {
                result = (string)Application.Current.Resources[key];

                if (!settings.Contains(key))
                {
                    settings.Add(key, result);
                }
                else
                {
                    settings[key] = result;
                }
                settings.Save();
            }

            return result;

        }

        // Event handler which runs after the feed is fully downloaded.
        private void webClient_XmlDownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                //reload was a success

                List<string> readStories = LoadReadStories();

                StringReader stringReader = new StringReader(e.Result);
                XmlReader xmlReader = XmlReader.Create(stringReader);
                SyndicationFeed feed = SyndicationFeed.Load(xmlReader);
                Dictionary<string, BitmapImage> icons = new Dictionary<string, BitmapImage>();
                XElement o;

                if (e.Error != null)
                {
                }
                else
                {
                    //downloaded ok
                    try
                    {
                        //feed ElementExtensions
                        //foreach (var v in feed.ElementExtensions)
                        //{
                        //    Debug.WriteLine(String.Format("f.ElementExtensions={0}", v.OuterName));
                        //}

                        //get the ranges from the ElementExtensions
                        o = feed.ElementExtensions[0].GetObject<XElement>();

                        string ranges = o.Value;

                        string[] r = ranges.Split(',');
                        _lower = Int16.Parse(r[0]);
                        _upper = Int16.Parse(r[1]);

                        //clear collections
                        _tags.Clear();
                        _stories.Clear();

                        //for each feed item
                        foreach (SyndicationItem i in feed.Items)
                        {
                            //create a Story
                            Story s = new Story();
                            try
                            {

                                //foreach (var v in i.ElementExtensions)
                                //{
                                //    Debug.WriteLine(String.Format("i.ElementExtensions={0}", v.OuterName));
                                //}

                                //get the icon url
                                XElement iconElement = i.ElementExtensions[0].GetObject<XElement>();
                                string iconUrl = iconElement.Value;

                                if (iconUrl.Length == 0)
                                {
                                    iconUrl = "NONE";
                                }

                                //get the short date
                                o = i.ElementExtensions[1].GetObject<XElement>();

                                string shortDate = o.Value;

                                //get the tags
                                o = i.ElementExtensions[2].GetObject<XElement>();

                                string tagElement = o.Value;

                                string[] allTags = tagElement.Split('|');

                                //for each tag associated with the story
                                foreach (string oneTag in allTags)
                                {
                                    if (!_tags.ContainsKey(oneTag))
                                    {
                                        Tag newTag = new Model.Tag();

                                        newTag.TagValue = oneTag;
                                        _tags.Add(oneTag, newTag);
                                    }

                                    _tags[oneTag].Stories.Add(s);
                                }

                                if(!_headless)
                                {
                                    if (!icons.ContainsKey(iconUrl))
                                    {
                                        icons.Add(iconUrl, GetImageFromIcon(iconUrl));
                                    }
                                    s.Icon = icons[iconUrl];
                                }

                                s.PublishDate = DateTime.Parse(shortDate);

                                s.ShortDate = shortDate;
                                s.Title = i.Title.Text;
                                s.Tags = allTags.ToList();
                                s.URL = i.Links[0].Uri.OriginalString;
                                s.IsRead = readStories.Contains(i.Title.Text);

                                _stories.Add(s.Title, s);
                            }
                            catch (Exception ee)
                            {
                                Debug.WriteLine(ee.Message);
                            }
                        }
                    }
                    catch (Exception)
                    {

                    }
                }

                CalculateSizesAndStates();

                _loaded = true;
                LoadCompletedEvent();
            }
            else
            {
                LoadFailedEvent();
            }
        }

        private void CalculateSizesAndStates()
        {
            List<int> counts = new List<int>();

            foreach (var k in _tags.Keys)
            {
                int c=_tags[k].Count();
                if (!counts.Contains(c))
                {
                    counts.Add(c);
                }
            }

            counts.Sort();

            foreach (var k in _tags.Keys)
            {
                //calculate font size
                int c = _tags[k].Count();

                if (c <= counts[0])
                    _tags[k].FontSize = 1;
                else if (c <= counts[counts.Count/4])
                {
                    _tags[k].FontSize = 2;
                }
                else if (c <= counts[counts.Count / 2])
                {
                    _tags[k].FontSize = 3;
                }
                else 
                {
                    _tags[k].FontSize = 4;
                }
            }
        }

        public int SortByNameAscending(string name1, string name2)
        {
            return name1.CompareTo(name2);
        }

        public string GetHtml()
        {
            StringBuilder b = new StringBuilder();

            //get the tags
            var allKeys = _tags.Keys.ToList();
            allKeys.Sort();
            Visibility darkBackgroundVisibility =
                (Visibility)Application.Current.Resources["PhoneDarkThemeVisibility"];

            //get the style part of the html
            b.Append("<STYLE TYPE=\"text/css\">");

            b.Append(".bodystyle {text-align: justify; background-color:#");
            
            // Determine the visibility of the dark background and write the theme background value.
            if (darkBackgroundVisibility == Visibility.Visible)
            {
                b.Append("000000");
            }
            else
            {
                b.Append("ffffff");
            }
            b.Append("; font-family:\"Segoe\"}");

            b.Append(".normal1 {font-size: 20; color: #00cc00; }");
            b.Append(".normal2 {font-size: 27; color: #00dd00; }");
            b.Append(".normal3 {font-size: 34; color: #00ee00; }");
            b.Append(".normal4 {font-size: 41; color: #00ff00; }");
            b.Append(".liked1 {font-size: 20; color: #cc0000; }");
            b.Append(".liked2 {font-size: 27; color: #dd0000; }");
            b.Append(".liked3 {font-size: 34; color: #ee0000; }");
            b.Append(".liked4 {font-size: 41; color: #ff0000; }");
            b.Append(".allread1 {font-size: 20; color: #888888; }");
            b.Append(".allread2 {font-size: 27; color: #888888; }");
            b.Append(".allread3 {font-size: 34; color: #888888; }");
            b.Append(".allread4 {font-size: 41; color: #888888; }");
            b.Append("a {text-decoration: none; }");
            b.Append("</STYLE>");

            //functions to get scroll position
            b.Append("<script>");
            b.Append("function getVerticalScrollPosition() {");
            b.Append("return document.body.scrollTop.toString();");
            b.Append("}");
            b.Append("function setVerticalScrollPosition(position) {");
            b.Append("document.body.scrollTop = position;");
            b.Append("}");
            b.Append("</script>");

            //add the body
            b.AppendFormat(@"<body class=""bodystyle"">");

            foreach (var tag in allKeys)
            {
                Tag t = _tags[tag];

                StringBuilder style = new StringBuilder();
                if (t.AllRead())
                {
                    style.AppendFormat(@"allread{0}", t.FontSize);
                }
                else if (_likedTags.Contains(tag))
                {
                    style.AppendFormat(@"liked{0}", t.FontSize);
                }
                else
                {
                    style.AppendFormat(@"normal{0}", t.FontSize);
                }
                b.Append(String.Format(@"<a href=""{0}"" ><span class=""{1}"">{0}</span></a>    ", tag, style.ToString()));
            }

            b.AppendFormat("</body>");

            return b.ToString();
        }

        public void ForceLoad(bool headless)
        {
            RefreshNewsFeed(headless);
        }

        public StoriesModel GetStoriesForTag(string tag)
        {
            StoriesModel model = new StoriesModel();

            List<Story> s = new List<Story>();

            model.Stories = new ObservableCollection<Story>(_stories.Values.Where(story => story.Tags.Contains(tag)));
            model.LikedText = (_likedTags.Contains(tag) ? "Liked" : "Unliked");
            model.Tag = tag;

            model.LikedTagColour= (IsTagLiked(tag) ? likedTagColour : unlikedTagColour); 
        

            return model;
        }

        public ViewerModel GetViewerForStory(string title)
        {
            ViewerModel model = new ViewerModel();

            model.Title = title;
            model.Uri = new Uri(_stories[title].URL);
            return model;
        }

        public IEnumerable<Story> GetUnreadStoriesForTag(string tag)
        {
            StoriesModel model = new StoriesModel();
            IEnumerable<Story> stories = null;

            List<Story> s = new List<Story>();

            stories = new ObservableCollection<Story>(_stories.Values.Where(story => story.Tags.Contains(tag) && !story.IsRead));

            return stories;
        }

        public IEnumerable<Story> GetUnreadStoriesForLikedTags()
        {
            List<Story> s = new List<Story>();
            List<string> tags = new List<string>();
            List<Story> result = new List<Story>();

            //all unread stories
            var stories = _stories.Values.Where(story => !story.IsRead);

            //list all their tags
            foreach (var story in stories)
            {
                //now count the liked ones
                foreach (var tag in story.Tags)
                {
                    if (_likedTags.Contains(tag))
                    {
                        result.Add(story);
                        break;
                    }
                }
            }
 
            return result;
        }

        public bool IsTagLiked(string tag)
        {
            return _likedTags.Contains(tag);
        }

        public void ToggleLikeTag(string tag)
        {
            if (_likedTags.Contains(tag))
            {
                _likedTags.Remove(tag);
            }
            else
            {
                _likedTags.Add(tag);
            }

            SaveLikedTags();
        }

        public void ReadStory(string title)
        {
            _stories[title].IsRead = true;
        }

        public void ReadAllStoriesForTag(string tag)
        {
            var c = _stories.Values.Where(story => story.Tags.Contains(tag));

            foreach (var s in c)
            {
                s.IsRead = true;
            }
        }

        public void ReadAllStories()
        {
            foreach (var s in _stories.Values)
            {
                s.IsRead = true;
            }
        }

        #region saving bits

        public void SaveLikedTags()
        {
            IsolatedStorageSettings settings = IsolatedStorageSettings.ApplicationSettings;

            if (!settings.Contains("likedTags"))
            {
                settings.Add("likedTags", _likedTags);
            }
            else
            {
                settings["likedTags"] = _likedTags;
            }
            settings.Save();
        }

        public void SaveReadStories()
        {
            IsolatedStorageSettings settings = IsolatedStorageSettings.ApplicationSettings;

            //get all the read stories
            List<Story> readStories = _stories.Values.Where(story => story.IsRead).ToList();

            //get a list of their titles
            List<string> readStoryTitles = new List<string>();
            foreach (var story in readStories)
            {
                readStoryTitles.Add(story.Title);
            }

            //save it
            if (!settings.Contains("readStories"))
            {
                settings.Add("readStories", readStoryTitles);
            }
            else
            {
                settings["readStories"] = readStoryTitles;
            }

            settings.Save();
        }

        public void SaveScrollPosition()
        {
            IsolatedStorageSettings settings = IsolatedStorageSettings.ApplicationSettings;

            if (!settings.Contains("scrollPosition"))
            {
                settings.Add("scrollPosition", _scrollPosition);
            }
            else
            {
                settings["scrollPosition"] = _scrollPosition;
            }
            settings.Save();
        }



        #endregion

        #region loading bits

        private void LoadLikedTags()
        {
            if (IsolatedStorageSettings.ApplicationSettings.Contains("likedTags"))
            {
                _likedTags = IsolatedStorageSettings.ApplicationSettings["likedTags"] as List<string>;
            }
        }


        public void LoadScrollPosition()
        {
            if (IsolatedStorageSettings.ApplicationSettings.Contains("scrollPosition"))
            {
                _scrollPosition =(int) IsolatedStorageSettings.ApplicationSettings["scrollPosition"];
            }
        }


        private List<string> LoadReadStories()
        {
            List<string> readStories = new List<string>();

            if (IsolatedStorageSettings.ApplicationSettings.Contains("readStories"))
            {
                readStories = IsolatedStorageSettings.ApplicationSettings["readStories"] as List<string>;
            }

            return readStories;
        }

        #endregion
    }
}
